tgauge 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d5d4baf3c8db65764294e937b63e35329622aa02
4
+ data.tar.gz: d91028cb4530550397075d31af4b18230c5cd9c9
5
+ SHA512:
6
+ metadata.gz: 190e0c6bc495d2d0f3e065471656f1c5ed0ac479624a20b3a7528529fbea151ed3cd109ad65b9c5a1c84a176e648a0475ae191d163e842b72e8b96911b5a192e
7
+ data.tar.gz: 8913871c7735f2c118ef88daaeb5a5344b2d7bf12d89b5efc5208567804fb5900709c02052058ccb61f4d5415a93406291a4e48f1497e5aa7e1cb0def388f1f7
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .byebug_history
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in TGauge.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # TGauge
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/TGauge`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'TGauge'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install TGauge
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ 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).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/TGauge.
36
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/TGauge.gemspec ADDED
@@ -0,0 +1,40 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "tgauge"
8
+ spec.version = TGauge::VERSION
9
+ spec.authors = ["Nam Kim"]
10
+ spec.email = ["nryulkim@gmail.com"]
11
+
12
+ spec.summary = %q{Simple Ruby ORM and MVC}
13
+ spec.description = %q{Allows you to build a simple webapp}
14
+ spec.homepage = "https://github.com/nryulkim/tgauge"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against " \
23
+ "public gem pushes."
24
+ end
25
+
26
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
+ f.match(%r{^(test|spec|features)/})
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+
33
+ spec.add_development_dependency "bundler", "~> 1.13"
34
+ spec.add_development_dependency "rake", "~> 10.0"
35
+ spec.add_runtime_dependency 'thor', '~> 0.19'
36
+ spec.add_runtime_dependency 'pg', '~> 0.18'
37
+ spec.add_runtime_dependency 'activesupport', '~> 4.2', '>= 4.2.5.1'
38
+ spec.add_runtime_dependency 'rack', '~> 1.6', '>=1.6.4'
39
+ spec.add_runtime_dependency 'fileutils', '~> 0.7'
40
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "TGauge"
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,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/bin/tgauge.rb ADDED
@@ -0,0 +1,119 @@
1
+ require 'rubygems'
2
+ require 'thor'
3
+ require 'active_support/inflector'
4
+ #Generates models, controllers, and migrations. Alias 'g'
5
+ class Generate < Thor
6
+ desc 'model <name>', 'generate a model.'
7
+ def model(name)
8
+ File.open('./app/models/#{name.downcase}.rb', 'w') do |f|
9
+ f.write("class #{name.capitalize} < TGauge::TRecordBase\n\n")
10
+ f.write("end\n")
11
+ f.write("#{name.capitalize}.finalize!")
12
+ end
13
+
14
+ migration("Create#{name.capitalize}}")
15
+ puts "#{name} model created"
16
+ end
17
+
18
+ desc 'controller <name>', 'generate a controller.'
19
+ def controller(name)
20
+ File.open('./app/controllers/#{name.downcase}.rb', 'w') do |f|
21
+ f.write("class #{name.capitalize}Controller < TGauge::TControllerBase\n\n")
22
+ f.write("end")
23
+ end
24
+
25
+ Dir.mkdir "./app/views/#{name.downcase}_controller"
26
+ puts "#{name} controller created"
27
+ end
28
+
29
+ desc 'migration <name>', 'generate an empty sql file.'
30
+ def migration(name)
31
+ #timestamp
32
+ ts = Time.now.to_i
33
+ filename = "#{ts}_#{name.underscore.downcase}"
34
+
35
+ File.open('./db/migrations/#{filename}.sql', 'w') do |f|
36
+ f.write "CREATE TABLE IF NOT EXISTS #{name} (\n"
37
+ f.write "\tid SERIAL PRIMARY KEY,\n"
38
+ f.write ')'
39
+ end
40
+ end
41
+ end
42
+
43
+ class Db < Thor
44
+ desc 'create', 'creates the DB'
45
+ def create
46
+ require_relative '../lib/db_connection'
47
+ TGauge::DBConnection.reset
48
+ puts 'db created!'
49
+ end
50
+
51
+ desc 'migrate', 'runs pending migrations'
52
+ def migrate
53
+ # Creates Version table if necessary,
54
+ # then runs needed migrations in order.
55
+ require_relative '../lib/db_connection'
56
+ TGauge::DBConnection.migrate
57
+ puts 'migrated!'
58
+ end
59
+
60
+ desc 'seed', 'seeds the DB'
61
+ def seed
62
+ require_relative '../lib/puffs'
63
+ TGauge::Seed.populate
64
+ puts 'db seeded!'
65
+ end
66
+
67
+ desc 'reset', 'resets the DB and seeds it'
68
+ def reset
69
+ create
70
+ migrate
71
+ seed
72
+ puts 'db reset!'
73
+ end
74
+ end
75
+
76
+ #top level commands
77
+ class CLI < Thor
78
+ register(Generate, 'generate', 'generate <command>', 'Generates a model, migration, or controller.')
79
+ register(Db, 'db', 'db <command>', 'Accesses commands for the DB.')
80
+
81
+ subcommand 'g', Generate
82
+
83
+ desc 'server', 'starts the WebBrick server'
84
+ def server
85
+ require_relative '../lib/tgauge.rb'
86
+ TGauge::Server.start
87
+ end
88
+
89
+ desc 'new', 'creates a new TGauge app'
90
+ def new(name)
91
+ Dir.mkdir "./#{name}"
92
+ Dir.mkdir "./#{name}/config"
93
+
94
+ File.open("./#{name}/config/database.yml", 'w') do |f|
95
+ f.write("database: #{name}")
96
+ end
97
+
98
+ Dir.mkdir "./#{name}/app"
99
+ Dir.mkdir "./#{name}/app/models"
100
+ Dir.mkdir "./#{name}/app/views"
101
+ Dir.mkdir "./#{name}/app/controllers"
102
+ File.open("./#{name}/app/controllers/application_controller.rb", 'w') do |f|
103
+ f.write File.read(File.expand_path('../../lib/template/app/controllers/application_controller.rb', __FILE__))
104
+ end
105
+ File.open("./#{name}/config/routes.rb", 'w') do |f|
106
+ f.write File.read(File.expand_path('../../lib/template/config/routes.rb', __FILE__))
107
+ end
108
+ Dir.mkdir "./#{name}/db"
109
+ Dir.mkdir "./#{name}/db/migrations"
110
+ File.open("./#{name}/db/seeds.rb", 'w') do |f|
111
+ f.write File.read(File.expand_path('../../lib/template/db/seeds.rb', __FILE__))
112
+ end
113
+ File.open("./#{name}/Gemfile", 'w') do |f|
114
+ f.write File.read(File.expand_path('../../lib/template/Gemfile', __FILE__))
115
+ end
116
+ end
117
+ end
118
+
119
+ CLI.start(ARGV)
@@ -0,0 +1,90 @@
1
+ require 'json'
2
+
3
+ class Flash
4
+ def initialize(req)
5
+ cookie_hash = req.cookies["_rails_lite_app_flash"]
6
+ @existing = cookie_hash ? JSON.parse(cookie_hash) : {}
7
+ @new = {}
8
+ end
9
+
10
+ def [](key)
11
+ merged = @existing.merge(@new)
12
+ merged[key.to_s]
13
+ end
14
+
15
+ def []=(key, val)
16
+ if @add_next
17
+ @existing[key.to_s] = val
18
+ @add_next = false
19
+ else
20
+ @new[key.to_s] = val
21
+ end
22
+ end
23
+
24
+ def now
25
+ @add_next = true
26
+ self
27
+ end
28
+
29
+ def store_flash(res)
30
+ out_cookie = @new.to_json
31
+ res.set_cookie('_rails_lite_app_flash', { path: "/", value: out_cookie })
32
+ end
33
+ end
34
+
35
+
36
+ # class Flash
37
+ # attr_reader :flashy_cookie
38
+ #
39
+ # def initialize(req)
40
+ # cookie_hash = req.cookies["_rails_lite_app_flash"]
41
+ # existing_cookie = JSON.parse(cookie_hash) if cookie_hash
42
+ # @flashy_cookie = existing_cookie ? existing_cookie : {}
43
+ # end
44
+ #
45
+ # def [](key)
46
+ # @flashy_cookie[key.to_s]
47
+ # end
48
+ #
49
+ # def []=(key, val)
50
+ # if @add_next
51
+ # old_keys.add(key.to_s)
52
+ # @add_next = false
53
+ # end
54
+ #
55
+ # @flashy_cookie[key] = val
56
+ # end
57
+ #
58
+ # def now
59
+ # @add_next = true
60
+ # self
61
+ # end
62
+ #
63
+ # def delete_old
64
+ # old_keys.each do |key|
65
+ # @flashy_cookie.delete(key)
66
+ # end
67
+ #
68
+ # @flashy_cookie.delete(:old)
69
+ # end
70
+ #
71
+ # # serialize the hash into json and save in a cookie
72
+ # # add to the responses cookies
73
+ # def store_flash(res)
74
+ # delete_old
75
+ # old_keys = @flashy_cookie.keys
76
+ # out_cookie = @flashy_cookie.to_json
77
+ # res.set_cookie('_rails_lite_app_flash', out_cookie)
78
+ # end
79
+ #
80
+ # private
81
+ # def old_keys
82
+ # @flashy_cookie[:old] ||= Set.new
83
+ # end
84
+ #
85
+ # def old_keys=(key_arr)
86
+ # old_keys
87
+ # keys.each { |key| @flash_cookie[:old].add(key) }
88
+ # old_keys.delete(:old)
89
+ # end
90
+ # end
@@ -0,0 +1,27 @@
1
+ require 'json'
2
+ require 'byebug'
3
+ class Session
4
+ # find the cookie for this app
5
+ # deserialize the cookie into a hash
6
+
7
+ def initialize(req)
8
+ cookie_hash = req.cookies["_rails_lite_app"]
9
+ existing_cookie = JSON.parse(cookie_hash) if cookie_hash
10
+ @cookie = existing_cookie ? existing_cookie : {}
11
+ end
12
+
13
+ def [](key)
14
+ @cookie[key]
15
+ end
16
+
17
+ def []=(key, val)
18
+ @cookie[key] = val
19
+ end
20
+
21
+ # serialize the hash into json and save in a cookie
22
+ # add to the responses cookies
23
+ def store_session(res)
24
+ out_cookie = @cookie.to_json
25
+ res.set_cookie('_rails_lite_app', { path: "/", value: out_cookie })
26
+ end
27
+ end
@@ -0,0 +1,91 @@
1
+ require 'active_support'
2
+ require 'active_support/inflector'
3
+ require 'active_support/core_ext'
4
+ require 'erb'
5
+ require_relative './session'
6
+ require_relative './flash'
7
+
8
+ module TGauge
9
+ class TControllerBase
10
+ @@defender = false
11
+ def self.protect_from_forgery
12
+ @@defender = true
13
+ end
14
+
15
+ attr_reader :req, :res, :params
16
+ # Setup the controller
17
+ def initialize(req, res, route_params = {})
18
+ @req = req
19
+ @res = res
20
+ @params = @req.params.merge(route_params)
21
+ end
22
+
23
+ # Helper method to alias @already_built_response
24
+ def already_built_response?
25
+ @rendered
26
+ end
27
+
28
+ # Set the response status code and header
29
+ def redirect_to(url)
30
+ @rendered ? raise {'Cannote render twice'} : @rendered = true
31
+ @res['Location'] = url
32
+ @res.status = 302
33
+ @session.store_session(res) if @session
34
+ end
35
+
36
+ # Populate the response with content.
37
+ # Set the response's content type to the given type.
38
+ # Raise an error if the developer tries to double render.
39
+ def render_content(content, content_type)
40
+ @rendered ? raise {'Cannote render twice'} : @rendered = true
41
+ @res.write(content)
42
+ @session.store_session(res) if @session
43
+ @flash.store_flash(res) if @flash
44
+ @res['Content-Type'] = content_type
45
+ end
46
+
47
+ # use ERB and binding to evaluate templates
48
+ # pass the rendered html to render_content
49
+ def render(template_name)
50
+ class_name = self.class.to_s.underscore
51
+ class_name.slice! "_controller"
52
+ view_path = "app/views/#{class_name}/#{template_name}.html.erb"
53
+ erb = ERB.new(File.read(view_path)).result(binding)
54
+ render_content(erb, 'text/html')
55
+ end
56
+
57
+ # method exposing a `Session` object
58
+ def session
59
+ @session ||= Session.new(@req)
60
+ end
61
+
62
+ def flash
63
+ @flash ||= Flash.new(@req)
64
+ end
65
+
66
+ # use this with the router to call action_name (:index, :show, :create...)
67
+ def invoke_action(name)
68
+ if @@defender && check_authenticity_token
69
+ @res.write("ATTACK ATTACK!! RUN AND HIDE!")
70
+ @res.status = 403
71
+ @res['Content-Type'] = "text/html"
72
+ else
73
+ self.send(name)
74
+ render(name) unless already_built_response?
75
+ end
76
+ end
77
+
78
+ def form_authenticity_token
79
+ flash[:_csurf_master_code] = SecureRandom.urlsafe_base64
80
+ end
81
+
82
+ private
83
+ def check_authenticity_token
84
+ !@req.get? && (master_code.nil? || master_code != @req.params["authenticity_token"])
85
+ end
86
+
87
+ def master_code
88
+ flash[:_csurf_master_code]
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,117 @@
1
+ class AssocOptions
2
+ attr_accessor(
3
+ :foreign_key,
4
+ :class_name,
5
+ :primary_key
6
+ )
7
+
8
+ def model_class
9
+ class_name.constantize
10
+ end
11
+
12
+ def table_name
13
+ model_class.table_name
14
+ end
15
+
16
+ end
17
+
18
+ class BelongsToOptions < AssocOptions
19
+ def initialize(name, options = {})
20
+ @foreign_key = options[:foreign_key] || (name.to_s.foreign_key).to_sym
21
+ @primary_key = options[:primary_key] || :id
22
+ @class_name = options[:class_name] || name.to_s.classify
23
+ end
24
+ end
25
+
26
+ class HasManyOptions < AssocOptions
27
+ def initialize(name, self_class_name, options = {})
28
+ @foreign_key = options[:foreign_key] || (self_class_name.foreign_key).to_sym
29
+ @primary_key = options[:primary_key] || :id
30
+ @class_name = options[:class_name] || name.to_s.singularize.classify
31
+ end
32
+ end
33
+
34
+ module Associatable
35
+ # Phase IIIb
36
+ def belongs_to(name, options = {})
37
+ options = BelongsToOptions.new(name, options)
38
+
39
+ define_method(name) do
40
+ fkey_val = self.send(options.foreign_key)
41
+ class_obj = options.model_class
42
+
43
+ class_obj.where(options.primary_key => fkey_val).first
44
+ end
45
+
46
+ assoc_options[name] = options
47
+ end
48
+
49
+ def has_many(name, options = {})
50
+ # ...
51
+ unless options.first[0] == :through
52
+ options = HasManyOptions.new(name, self.to_s, options)
53
+
54
+ define_method(name) do
55
+ class_obj = options.model_class
56
+ pr_key = self.send(options.primary_key)
57
+ class_obj.where(options.foreign_key => pr_key)
58
+ end
59
+
60
+ assoc_options[name] = options
61
+ else
62
+ options[:source] ||= name
63
+ through(name, options[:through], options[:source])
64
+ end
65
+ end
66
+
67
+ def through(name, through_name, source_name)
68
+ define_method(name) do
69
+ through_options = self.class.assoc_options[through_name]
70
+ source_options = through_options.model_class.assoc_options[source_name]
71
+
72
+ if through_options.is_a?(BelongsToOptions)
73
+ # A to B has a belongs_to relationship
74
+ a_test_key = self.send(through_options.foreign_key)
75
+ b_column = through_options.primary_key
76
+ else
77
+ # A to B has a has_many relationship
78
+ a_test_key = self.send(through_options.primary_key)
79
+ b_column = through_options.foreign_key
80
+ end
81
+
82
+ if source_options.is_a?(BelongsToOptions)
83
+ # B to C has a belongs_to relationship
84
+ b_test_key = source_options.foreign_key
85
+ c_column = source_options.primary_key
86
+ else
87
+ # B to C has a has_many relationship
88
+ b_test_key = source_options.primary_key
89
+ c_column = source_options.foreign_key
90
+ end
91
+ source_table = source_options.table_name
92
+ through_table = through_options.table_name
93
+
94
+ all_data = DBConnection.execute(<<-SQL)
95
+ SELECT
96
+ #{source_table}.*
97
+ FROM
98
+ #{through_table}
99
+ JOIN
100
+ #{source_table} ON #{source_table}.#{c_column} = #{through_table}.#{b_test_key}
101
+ WHERE
102
+ #{through_table}.#{b_column} = #{a_test_key}
103
+ SQL
104
+
105
+ source_options.model_class.parse_all(all_data)
106
+ end
107
+ end
108
+
109
+ def has_one (name, options = {})
110
+ options[:source] ||= name
111
+ through(name, options[:through], options[:source])
112
+ end
113
+
114
+ def assoc_options
115
+ @assoc_options ||= {}
116
+ end
117
+ end
@@ -0,0 +1,47 @@
1
+ module Searchable
2
+ def where(params)
3
+ relation_obj ||= LazyRelation.new(self, self.table_name, {})
4
+ relation_obj.where(params)
5
+ relation_obj
6
+ end
7
+ end
8
+
9
+ class LazyRelation
10
+ def initialize(klass, table_name, command_hash)
11
+ @klass = klass
12
+ @table_name = table_name
13
+ @command_hash = command_hash
14
+ end
15
+
16
+ def where(params)
17
+ @command_hash = params.merge(@command_hash)
18
+ self
19
+ end
20
+
21
+ def where_str
22
+ @command_hash.map do |col, val|
23
+ val = "'#{val}'" if val.is_a?(String)
24
+ "#{@table_name}.#{col} = #{val}"
25
+ end.join(" AND ")
26
+ end
27
+
28
+
29
+ def execute
30
+ all_attrs = DBConnection.execute(<<-SQL)
31
+ SELECT
32
+ #{@table_name}.*
33
+ FROM
34
+ #{@table_name}
35
+ WHERE
36
+ #{where_str}
37
+ SQL
38
+
39
+ all_attrs.map { |atts| @klass.new(atts) }
40
+ end
41
+
42
+ def method_missing(method_name, *args)
43
+ obj = execute
44
+ obj[0].send(method_name, *args)
45
+ end
46
+
47
+ end