├── src ├── mustafa │ ├── base │ │ ├── view │ │ │ ├── Notfoundview.ecr │ │ │ ├── Notfoundview.cr │ │ │ └── JSONview.cr │ │ └── controller │ │ │ └── Notfoundcontroller.cr │ ├── version.cr │ ├── classextension │ │ ├── string.cr │ │ └── hash.cr │ ├── helper │ │ └── library.cr │ ├── library │ │ ├── json.cr │ │ ├── crypto.cr │ │ ├── session.cr │ │ ├── input.cr │ │ ├── rest.cr │ │ ├── restserver.cr │ │ ├── db │ │ │ ├── dummy.cr │ │ │ ├── mysql.cr │ │ │ └── sqllite.cr │ │ ├── log.cr │ │ └── mail.cr │ ├── config │ │ ├── constants.cr │ │ └── config.cr │ ├── core │ │ ├── helper │ │ │ ├── view.cr │ │ │ ├── session.cr │ │ │ ├── encoder.cr │ │ │ ├── model.cr │ │ │ ├── library.cr │ │ │ ├── route.cr │ │ │ └── controller.cr │ │ ├── library.cr │ │ ├── http │ │ │ ├── route_handler.cr │ │ │ ├── request.cr │ │ │ ├── response.cr │ │ │ └── session_handler.cr │ │ ├── router.cr │ │ ├── control.cr │ │ ├── model.cr │ │ ├── db.cr │ │ ├── app.cr │ │ ├── view.cr │ │ ├── controller.cr │ │ └── loader.cr │ └── input │ │ ├── param.cr │ │ ├── get.cr │ │ ├── post.cr │ │ ├── put.cr │ │ └── delete.cr └── mustafa.cr ├── spec ├── spec_helper.cr └── mustafa_spec.cr ├── shard.yml ├── snippets.md ├── LICENSE └── README.md /src/mustafa/base/view/Notfoundview.ecr: -------------------------------------------------------------------------------- 1 | Controller bulunamadı! -------------------------------------------------------------------------------- /spec/spec_helper.cr: -------------------------------------------------------------------------------- 1 | require "spec" 2 | require "../src/mustafa" 3 | -------------------------------------------------------------------------------- /src/mustafa/version.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | VERSION = "0.1.2" 3 | end 4 | -------------------------------------------------------------------------------- /src/mustafa/classextension/string.cr: -------------------------------------------------------------------------------- 1 | class String 2 | def self.empty : String 3 | "" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /src/mustafa/classextension/hash.cr: -------------------------------------------------------------------------------- 1 | class Hash(K, V) 2 | def >(key : K) : V 3 | fetch(key) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /src/mustafa/helper/library.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Helper 3 | class Library 4 | 5 | 6 | 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /spec/mustafa_spec.cr: -------------------------------------------------------------------------------- 1 | require "./spec_helper" 2 | 3 | describe Mustafa do 4 | # TODO: Write tests 5 | 6 | it "works" do 7 | false.should eq(true) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /src/mustafa/library/json.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Library 3 | class JSON 4 | 5 | def load 6 | 7 | end 8 | 9 | end 10 | end 11 | end -------------------------------------------------------------------------------- /src/mustafa/library/crypto.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Library 3 | class Crypto < Mustafa::Core::Library 4 | 5 | INSTANCE = Crypto.new 6 | 7 | init Crypto 8 | 9 | end 10 | end 11 | end -------------------------------------------------------------------------------- /src/mustafa/base/controller/Notfoundcontroller.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | class Notfoundcontroller < Core::Controller 3 | init Notfoundcontroller 4 | 5 | action "index" do 6 | Core.loader.view(self, Notfoundview) 7 | end 8 | 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /src/mustafa/base/view/Notfoundview.cr: -------------------------------------------------------------------------------- 1 | require "ecr" 2 | require "ecr/macros" 3 | 4 | module Mustafa 5 | class Notfoundview < Core::View 6 | def load 7 | 8 | end 9 | 10 | ECR.def_to_s "./lib/mustafa/src/mustafa/base/view/Notfoundview.ecr" 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /src/mustafa/config/constants.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Constant 3 | DB_CONNECTION_STRING = "mysql://root@localhost/mustafa" 4 | DB_CLASS = Mustafa::Library::Database::Mysql 5 | 6 | SESSION_KEY = "session_cr" 7 | SESSION_SECRET_KEY = "Mustafa" 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /shard.yml: -------------------------------------------------------------------------------- 1 | name: mustafa 2 | version: 0.1.0 3 | 4 | authors: 5 | - Güven Cenan Güvenal 6 | 7 | 8 | crystal: 0.19.4 9 | 10 | license: MIT 11 | 12 | dependencies: 13 | mysql: 14 | github: crystal-lang/crystal-mysql 15 | sqlite3: 16 | github: crystal-lang/crystal-sqlite3 17 | smtp: 18 | github: raydf/smtp.cr 19 | -------------------------------------------------------------------------------- /src/mustafa/base/view/JSONview.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | class JSONview < Core::View 3 | 4 | def initialize(output : String) 5 | @out = output 6 | end 7 | 8 | def load 9 | @content_type = "application/json" 10 | @content_length = @out.size 11 | end 12 | 13 | def to_s 14 | @out 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /src/mustafa/library/session.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Library 3 | class Session < Core::Library 4 | init Session, Core::Library::Scope::Singleton 5 | 6 | def get(key : String) : String | Nil 7 | Core::Helper.session.session_data[key]? 8 | end 9 | 10 | def set(key : String, value : String) 11 | Core::Helper.session.session_data[key] = value 12 | end 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /src/mustafa/config/config.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Config 3 | DEFAULT_CONTROLLER = "Welcomecontroller" 4 | MODULE_NAME = "Mustafa" 5 | VIEW_PATH = "src/mustafa/view" # ./Mustafa::Config::VIEW_PATH/ecr_file.ecr -> ./src/mustafa/view/ecr_file.ecr 6 | CONTROL_PATH = "src/mustafa/view" 7 | LOG_PATH = "src/#{Config::MODULE_NAME.downcase}/logfiles" 8 | 9 | SYSTEM_LOG_PATH = "lib/mustafa/logfiles" 10 | 11 | LOCALHOST_ADDRESS = "127.0.0.1" 12 | end 13 | end -------------------------------------------------------------------------------- /src/mustafa.cr: -------------------------------------------------------------------------------- 1 | require "./mustafa/*" 2 | require "./mustafa/core/*" 3 | require "./mustafa/input/*" 4 | require "./mustafa/config/*" 5 | require "./mustafa/library/*" 6 | require "./mustafa/library/db/*" 7 | require "./mustafa/core/helper/*" 8 | require "./mustafa/core/http/*" 9 | require "./mustafa/classextension/*" 10 | require "./mustafa/base/**" 11 | require "db" 12 | require "mysql" 13 | require "sqlite3" 14 | 15 | include Mustafa 16 | 17 | module Mustafa 18 | 19 | def self.run(port = 8080) 20 | Core.app.serve port 21 | end 22 | 23 | end 24 | -------------------------------------------------------------------------------- /src/mustafa/core/helper/view.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Core 3 | module Helper 4 | class View 5 | INSTANCE = View.new 6 | 7 | def add_param(key : String, value : String, obj : Core::View) 8 | obj.add_param(key, value) 9 | end 10 | 11 | def add_array(key : String, value : Array(String), obj : Core::View) 12 | obj.add_array(key, value) 13 | end 14 | 15 | end 16 | 17 | def self.view 18 | yield View::INSTANCE 19 | end 20 | 21 | def self.view 22 | View::INSTANCE 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /src/mustafa/core/library.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Core 3 | ### 4 | # this class is abstract to create library class. 5 | ### 6 | abstract class Library 7 | 8 | enum Scope 9 | Singleton 10 | Request 11 | end 12 | ### 13 | # this macro is to register libraries 14 | ### 15 | macro init(library_name, scope = Scope::Request) 16 | Mustafa::Core::Helper.library.register_library {{library_name.id}}, {{scope.id}} 17 | end 18 | 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /snippets.md: -------------------------------------------------------------------------------- 1 | # Visual Studio Code snippets for Mustafa MVC 2 | 3 | ###Controller 4 | 5 | `init controller` 6 | 7 | this snippet is to create controller class template. 8 | 9 | `load_view` 10 | 11 | `load_model` 12 | 13 | ###View 14 | 15 | `init view` 16 | 17 | this snippet is to create view class template. 18 | 19 | ###Model 20 | 21 | `init model` 22 | 23 | this snippet is to create model class template. 24 | 25 | ###Control 26 | 27 | `init control` 28 | 29 | this snippet is to create control class template. 30 | 31 | `control_prop` 32 | 33 | this snippet is to add control property on your control's html 34 | 35 | Example : 36 | 37 | ``` 38 | "" 41 | ``` 42 | -------------------------------------------------------------------------------- /src/mustafa/core/helper/session.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Core 3 | module Helper 4 | class Session 5 | INSTANCE = Session.new 6 | 7 | property session_data : Hash(String, String) 8 | 9 | def initialize 10 | @session_data = Hash(String, String).new 11 | end 12 | 13 | def set_session_data(session_data : Hash(String, String)) 14 | @session_data = session_data 15 | end 16 | 17 | def get_session_data : Hash(String, String) 18 | @session_data 19 | end 20 | end 21 | 22 | def self.session 23 | yield Session::INSTANCE 24 | end 25 | 26 | def self.session 27 | Session::INSTANCE 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /src/mustafa/library/input.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Library 3 | class Input 4 | 5 | INSTANCE = Input.new 6 | 7 | def get(key : String) : String 8 | Input.get[key] 9 | end 10 | 11 | def get(key : String, index : Int32) : Array(String) 12 | Input.get[key, index] 13 | end 14 | 15 | def post(key : String) : String 16 | Input.post[key] 17 | end 18 | 19 | def post_array(key : String) : Array(String) 20 | Input.post[key] 21 | end 22 | 23 | def params(index : Int32) : String 24 | Input.params[index] 25 | end 26 | end 27 | 28 | def self.input 29 | yield Input::INSTANCE 30 | end 31 | 32 | def self.input 33 | Input::INSTANCE 34 | end 35 | 36 | end 37 | end -------------------------------------------------------------------------------- /src/mustafa/core/http/route_handler.cr: -------------------------------------------------------------------------------- 1 | require "http" 2 | 3 | module Mustafa 4 | module Http 5 | class Handler 6 | 7 | include HTTP::Handler 8 | 9 | def call(context) 10 | if Core::Helper.controller.controller_name? context.request.path 11 | Core::Helper.session.set_session_data context.session 12 | 13 | Core.router.route_controller context 14 | 15 | context.session = Core::Helper.session.get_session_data 16 | 17 | context 18 | else 19 | ## 20 | # if next handler is already exist 21 | ## 22 | if next_handler = @next 23 | next_handler.call(context) 24 | end 25 | end 26 | end 27 | end 28 | end 29 | end -------------------------------------------------------------------------------- /src/mustafa/core/http/request.cr: -------------------------------------------------------------------------------- 1 | require "http" 2 | 3 | module Mustafa 4 | module Http 5 | class Request 6 | property method : String 7 | property headers : HTTP::Headers 8 | property body : IO? 9 | property body_str : String 10 | property version : String 11 | property path : String 12 | property query_string : String? 13 | property query_params : HTTP::Params 14 | 15 | def initialize(request : HTTP::Request) 16 | @method = request.method 17 | @path = request.path.to_s 18 | @headers = request.headers 19 | @body = request.body 20 | @body_str = "" 21 | @version = request.version 22 | @query_string = request.query 23 | @query_params = request.query_params 24 | end 25 | end 26 | end 27 | end -------------------------------------------------------------------------------- /src/mustafa/core/helper/encoder.cr: -------------------------------------------------------------------------------- 1 | require "openssl/hmac" 2 | require "base64" 3 | require "openssl/md5" 4 | 5 | module Mustafa 6 | module Core 7 | module Helper 8 | class Encoder 9 | class BadData < Exception 10 | end 11 | 12 | def initialize(@secret : String) 13 | end 14 | 15 | def encode(data : String) 16 | Base64.encode(data) + "--" + generate_signature(data) 17 | end 18 | 19 | def decode(data : String) 20 | data, signature = data.split("--") 21 | Base64.decode_string(data).tap do |data| 22 | raise BadData.new("Signature does not match") unless generate_signature(data) == signature 23 | end 24 | end 25 | 26 | private def generate_signature(data) 27 | OpenSSL::HMAC.hexdigest(:sha1, @secret, data) 28 | end 29 | 30 | def hex_digest(data) 31 | OpenSSL::MD5.hash(data) 32 | end 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /src/mustafa/input/param.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Input 3 | class PARAMS 4 | INSTANCE = PARAMS.new 5 | 6 | getter :params 7 | 8 | def initialize 9 | @params = [] of String 10 | end 11 | 12 | ### 13 | # this method return only a get query parameter 14 | # 15 | ### 16 | def [](index : Int32) : String 17 | if @params.size > index 18 | @params[index] 19 | else 20 | "" 21 | end 22 | end 23 | 24 | ### 25 | # this method is implement for develop envoriment 26 | ### 27 | def set_params(params_array : Array(String)) 28 | @params = params_array 29 | end 30 | end 31 | 32 | def self.params 33 | yield PARAMS::INSTANCE 34 | end 35 | 36 | def self.params 37 | PARAMS::INSTANCE 38 | end 39 | 40 | end 41 | end -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Güven Cenan Güvenal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/mustafa/library/rest.cr: -------------------------------------------------------------------------------- 1 | require "http/client" 2 | require "json" 3 | 4 | module Mustafa 5 | module Library 6 | class REST 7 | INSTANCE = Session.new 8 | 9 | def get(url : String) : JSON 10 | HTTP::Client.get(url) do |response| 11 | if response.status.code == 200 12 | 13 | 14 | end 15 | end 16 | end 17 | 18 | def get(url : String, params : Hash(String, String)) : JSON 19 | 20 | end 21 | 22 | def post(url : String) : JSON 23 | 24 | end 25 | 26 | def post(url : String, params : Hash(String, String)) : JSON 27 | 28 | end 29 | 30 | def put(url : String) : JSON 31 | 32 | end 33 | 34 | def put(url : String, params : Hash(String, String)) : JSON 35 | 36 | end 37 | 38 | def delete(url : String) : JSON 39 | 40 | end 41 | 42 | def delete(url : String, params : Hash(String, String)) : JSON 43 | 44 | end 45 | 46 | end 47 | 48 | def self.rest 49 | yield REST::INSTANCE 50 | end 51 | 52 | def self.rest 53 | REST::INSTANCE 54 | end 55 | end 56 | end -------------------------------------------------------------------------------- /src/mustafa/library/restserver.cr: -------------------------------------------------------------------------------- 1 | require "http/client" 2 | require "json" 3 | 4 | module Mustafa 5 | module Library 6 | class RESTServer 7 | INSTANCE = Session.new 8 | 9 | def get(url : String) : JSON 10 | HTTP::Client.get(url) do |response| 11 | if response.status.code == 200 12 | 13 | 14 | end 15 | end 16 | end 17 | 18 | def get(url : String, params : Hash(String, String)) : JSON 19 | 20 | end 21 | 22 | def post(url : String) : JSON 23 | 24 | end 25 | 26 | def post(url : String, params : Hash(String, String)) : JSON 27 | 28 | end 29 | 30 | def put(url : String) : JSON 31 | 32 | end 33 | 34 | def put(url : String, params : Hash(String, String)) : JSON 35 | 36 | end 37 | 38 | def delete(url : String) : JSON 39 | 40 | end 41 | 42 | def delete(url : String, params : Hash(String, String)) : JSON 43 | 44 | end 45 | 46 | end 47 | 48 | def self.rest_server 49 | yield REST::INSTANCE 50 | end 51 | 52 | def self.rest_server 53 | REST::INSTANCE 54 | end 55 | end 56 | end -------------------------------------------------------------------------------- /src/mustafa/core/router.cr: -------------------------------------------------------------------------------- 1 | require "http" 2 | 3 | module Mustafa 4 | module Core 5 | ### 6 | # this class for register/load controller 7 | # 8 | # this class is singletone Mustafa::Core.route 9 | ### 10 | class Router 11 | INSTANCE = Router.new 12 | 13 | ### 14 | # Controller register and load methods 15 | # 16 | # this method use polymorphysm 17 | ### 18 | def route_controller(context : HTTP::Server::Context) 19 | Helper.route.set_query_params context.request.method.to_s, context.request.query_params.to_s 20 | path_parse_array = Helper.route.path_split context.request.path 21 | Helper.route.set_url_params context.request.path 22 | 23 | if Helper.controller.__controllers.has_key? path_parse_array[0] 24 | controller_obj = Helper.controller.__controllers[path_parse_array[0]] 25 | controller_obj.run_action path_parse_array[1] 26 | 27 | _view = controller_obj.load_view 28 | context.response.content_type = _view.content_type 29 | context.response.status_code = _view.status_code 30 | context.response.print _view.to_s 31 | else 32 | context.response.status_code = 404 33 | context.response.print "404 Controller Bulunamadı" 34 | end 35 | end 36 | end 37 | 38 | def self.router 39 | yield Router::INSTANCE 40 | end 41 | 42 | def self.router 43 | Router::INSTANCE 44 | end 45 | 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /src/mustafa/core/control.cr: -------------------------------------------------------------------------------- 1 | require "ecr" 2 | require "ecr/macros" 3 | 4 | module Mustafa 5 | module Core 6 | abstract class Control 7 | 8 | ### 9 | # This macro is for initialize ECR Control class 10 | # 11 | # init "Welcomecontrol.ecr" 12 | ### 13 | macro init(filename, props, script = "", props_default = {css: ""}) 14 | {% for name, type in props %} 15 | property {{name.id}} : {{type.id}}? 16 | {% end %} 17 | 18 | def initialize 19 | @script = {{script}} 20 | {% for name, value in props_default %} 21 | @{{name.id}} = {{value}} 22 | {% end %} 23 | end 24 | 25 | def load( 26 | {% for name, type in props %} 27 | {{name.id}} : {{type.id}}, 28 | {% end %} 29 | ) 30 | {% for name, type in props %} 31 | @{{name.id}} = {{name.id}} 32 | {% end %} 33 | return self 34 | end 35 | 36 | def to_s(__io__) 37 | ECR.embed "./#{Config::CONTROL_PATH.id}/#{{{filename}}}", "__io__" 38 | end 39 | end 40 | 41 | ### 42 | # 43 | ### 44 | property script = "" 45 | property css = "" 46 | 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /src/mustafa/library/db/dummy.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Library 3 | module Database 4 | ### 5 | # this class DUMMY database connection that is to say Null Database 6 | ### 7 | class Dummy < Core::IDatabase 8 | 9 | def initialize(connection_string : String) 10 | 11 | end 12 | 13 | def query(query_string : String) : Array(Hash(String, String)) 14 | Array(Hash(String, String)).new 15 | end 16 | 17 | def select(select_columns : String, from : String, where = "true") : Array(Hash(String, String)) 18 | Array(Hash(String, String)).new 19 | end 20 | 21 | def select_all(from : String, where = "true") : Array(Hash(String, String)) 22 | Array(Hash(String, String)).new 23 | end 24 | 25 | def select_table_count(table : String) 26 | 0 27 | end 28 | 29 | def select_count(table : String, where : String) 30 | 0 31 | end 32 | 33 | def insert(table : String, values : Array(DB::Any)) 34 | 35 | end 36 | 37 | def delete(table : String, where = "false") 38 | 39 | end 40 | 41 | def close 42 | 43 | end 44 | end 45 | end 46 | end 47 | end -------------------------------------------------------------------------------- /src/mustafa/core/http/response.cr: -------------------------------------------------------------------------------- 1 | require "http" 2 | 3 | module Mustafa 4 | module Http 5 | class Response 6 | property output : String 7 | property content_type : String 8 | property content_length : Int32 9 | 10 | def initialize 11 | @output = "" 12 | @content_type = "text/html" 13 | @content_length = 1000 14 | end 15 | 16 | def initialize (output : String, content_type : String, content_length : Int32) 17 | @output = output 18 | @content_type = content_type 19 | @content_length = content_length 20 | end 21 | 22 | def text (str : String) 23 | @output = str 24 | end 25 | 26 | def html (str : String) 27 | @output = str 28 | @content_type = "text/html" 29 | end 30 | 31 | def json (str : String) 32 | @output = str 33 | @content_type = "application/json" 34 | end 35 | 36 | end 37 | end 38 | end 39 | 40 | class HTTP::Server::Response 41 | def set_cookie(key : String, cookie : HTTP::Cookie) 42 | headers.add("Set-Cookie", cookie.to_set_cookie_header) 43 | end 44 | 45 | def set_cookie(key : String, value) 46 | set_cookie(key, HTTP::Cookie.new(key, value)) 47 | end 48 | 49 | def delete_cookie(key : String) 50 | set_cookie(key, HTTP::Cookie.new(key, "", expires: Time.new(1970, 1, 1))) 51 | end 52 | end -------------------------------------------------------------------------------- /src/mustafa/library/log.cr: -------------------------------------------------------------------------------- 1 | require "file_utils.cr" 2 | 3 | module Mustafa 4 | 5 | enum LogType 6 | System 7 | Application 8 | end 9 | 10 | module Library 11 | class Log 12 | INSTANCE = Log.new 13 | 14 | def initialize 15 | if !File.directory?(Config::LOG_PATH) 16 | FileUtils.mkdir(Config::LOG_PATH) 17 | end 18 | 19 | if !File.directory?(Config::SYSTEM_LOG_PATH) 20 | FileUtils.mkdir(Config::SYSTEM_LOG_PATH) 21 | end 22 | end 23 | 24 | def add(log_val : String, type = LogType::Application.value) 25 | time = Time.now 26 | file_name = "#{time.year}-#{time.month}-#{time.day}|#{time.hour}-#{time.minute}.log" 27 | 28 | File.write(type == LogType::Application.value ? "#{Config::LOG_PATH}/#{file_name}" : "#{Config::SYSTEM_LOG_PATH}/#{file_name}", "Error: #{log_val}\n") 29 | end 30 | 31 | def clear_system_log 32 | File.delete(Config::SYSTEM_LOG_PATH) 33 | rescue ex : Errno 34 | INSTANCE.log("System Error : #{ex.message}", LogType::System.value) 35 | end 36 | 37 | def clear 38 | File.delete(Config::LOG_PATH) 39 | rescue ex : Errno 40 | INSTANCE.log("Application Error : #{ex.message}") 41 | end 42 | end 43 | 44 | def self.log 45 | yield Log::INSTANCE 46 | end 47 | 48 | def self.log 49 | Log::INSTANCE 50 | end 51 | end 52 | end -------------------------------------------------------------------------------- /src/mustafa/core/model.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Core 3 | abstract class Model 4 | 5 | enum Scope 6 | Singleton 7 | Request 8 | Prototype 9 | end 10 | 11 | macro init(model_class, scope = Scope::Request) 12 | Mustafa::Core::Helper.model.register_model {{model_class.id}}, {{scope.id}} 13 | end 14 | 15 | # getter entity_name = "" 16 | 17 | # def initialize(entity_name : String) 18 | 19 | # end 20 | 21 | # def select(select_columns : String, where = "true") : Array(Hash(String, String)) 22 | # _retval = Array(Hash(Sring, String)).new 23 | 24 | # Core.loader.db do |db| 25 | # _retval = db.select(select_columns, @entity_name, where) 26 | # end 27 | 28 | # _retval 29 | # end 30 | 31 | # def select_all(where = "true") : Array(Hash(String, String)) 32 | # _retval = Array(Hash(String, String)).new 33 | 34 | # Core.loader.db do |db| 35 | # _retval = db.select_all(@entity_name, where) 36 | # end 37 | 38 | # _retval 39 | # end 40 | 41 | # def select_table_count() 42 | # 0 43 | # end 44 | 45 | # def select_count(where : String) 46 | # 0 47 | # end 48 | 49 | # def insert(values : Array(DB::Any)) 50 | # Core.loader.db do |db| 51 | # db.insert(@entity_name, values) 52 | # end 53 | # end 54 | 55 | # def delete(where = "false") 56 | # Core.loader.db do |db| 57 | # db.delete(@entity_name, where) 58 | # end 59 | # end 60 | 61 | #this class for model 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /src/mustafa/core/db.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Core 3 | ### 4 | # this class is abstract to create database class. 5 | ### 6 | abstract class IDatabase 7 | # Pls open connection on initialize method 8 | 9 | ### 10 | # this method is to run query 11 | ### 12 | abstract def query(query_string : String) : Array(Hash(String, String)) 13 | 14 | ### 15 | # this method is to select following columns from table 16 | ### 17 | abstract def select(select_columns : String, from : String, where = "true") : Array(Hash(String, String)) 18 | 19 | ### 20 | # this method is to select * from table 21 | ### 22 | abstract def select_all(from : String, where = "true") : Array(Hash(String, String)) 23 | 24 | ### 25 | # this method get table row count 26 | ### 27 | abstract def select_table_count(table : String) 28 | 29 | ### 30 | # this method get "where" following table row count 31 | ### 32 | abstract def select_count(table : String, where : String) 33 | 34 | ### 35 | # insert table 36 | ### 37 | abstract def insert(table : String, values : Array(DB::Any)) 38 | 39 | ### 40 | # delete from table where 41 | ### 42 | abstract def delete(table : String, where = "false") 43 | 44 | ### 45 | # close connection 46 | # HINT: Pls open connection with initialize method 47 | ### 48 | abstract def close 49 | 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /src/mustafa/input/get.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Input 3 | class GET 4 | INSTANCE = GET.new 5 | 6 | getter :params 7 | 8 | def initialize 9 | @params = {} of String => Array(String) 10 | end 11 | 12 | ### 13 | # this method return get query parameters 14 | # 15 | ### 16 | def [](key : String, index : Int32) : String 17 | if @params.has_key?(key) 18 | if @params[key].size > index 19 | @params[key][index] 20 | else 21 | "" 22 | end 23 | else 24 | "" 25 | end 26 | end 27 | 28 | ### 29 | # this method return only a get query parameter 30 | # 31 | ### 32 | def [](key : String) : String 33 | if @params.has_key?(key) 34 | @params[key][0] 35 | else 36 | "" 37 | end 38 | end 39 | 40 | ### 41 | # this method is implement for develop envoriment 42 | ### 43 | def set_with_query(query : String) 44 | @params = {} of String => Array(String) 45 | 46 | HTTP::Params.parse(query) do |key, value| 47 | ary = @params[key] ||= [] of String 48 | ary.push value 49 | end 50 | end 51 | end 52 | 53 | def self.get 54 | yield GET::INSTANCE 55 | end 56 | 57 | def self.get 58 | GET::INSTANCE 59 | end 60 | 61 | end 62 | end -------------------------------------------------------------------------------- /src/mustafa/input/post.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Input 3 | class POST 4 | INSTANCE = POST.new 5 | 6 | getter :params 7 | 8 | def initialize 9 | @params = {} of String => Array(String) 10 | end 11 | 12 | ### 13 | # this method return post query parameters 14 | # 15 | ### 16 | def [](key : String, index : Int32) : String 17 | if @params.has_key?(key) 18 | if @params[key].size > index 19 | @params[key][index] 20 | else 21 | "" 22 | end 23 | else 24 | "" 25 | end 26 | end 27 | 28 | ### 29 | # this method return only a post query parameter 30 | # 31 | ### 32 | def [](key : String) : String 33 | if @params.has_key?(key) 34 | @params[key][0] 35 | else 36 | "" 37 | end 38 | end 39 | 40 | ### 41 | # this method is implement for develop envoriment 42 | ### 43 | def set_with_query(query : String) 44 | @params = {} of String => Array(String) 45 | 46 | HTTP::Params.parse(query) do |key, value| 47 | ary = @params[key] ||= [] of String 48 | ary.push value 49 | end 50 | end 51 | end 52 | 53 | def self.post 54 | yield POST::INSTANCE 55 | end 56 | 57 | def self.post 58 | POST::INSTANCE 59 | end 60 | 61 | end 62 | end -------------------------------------------------------------------------------- /src/mustafa/input/put.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Input 3 | class PUT 4 | INSTANCE = PUT.new 5 | 6 | getter :params 7 | 8 | def initialize 9 | @params = {} of String => Array(String) 10 | end 11 | 12 | ### 13 | # this method return post query parameters 14 | # 15 | ### 16 | def [](key : String, index : Int32) : String 17 | if @params.has_key?(key) 18 | if @params[key].size > index 19 | @params[key][index] 20 | else 21 | "" 22 | end 23 | else 24 | "" 25 | end 26 | end 27 | 28 | ### 29 | # this method return only a post query parameter 30 | # 31 | ### 32 | def [](key : String) : String 33 | if @params.has_key?(key) 34 | @params[key][0] 35 | else 36 | "" 37 | end 38 | end 39 | 40 | ### 41 | # this method is implemented for development envoriment 42 | ### 43 | def set_with_query(query : String) 44 | @params = {} of String => Array(String) 45 | 46 | HTTP::Params.parse(query) do |key, value| 47 | ary = @params[key] ||= [] of String 48 | ary.push value 49 | end 50 | end 51 | end 52 | 53 | def self.put 54 | yield PUT::INSTANCE 55 | end 56 | 57 | def self.put 58 | PUT::INSTANCE 59 | end 60 | 61 | end 62 | end -------------------------------------------------------------------------------- /src/mustafa/core/http/session_handler.cr: -------------------------------------------------------------------------------- 1 | require "http" 2 | require "json" 3 | 4 | module Mustafa 5 | module Http 6 | module Session 7 | class Handler 8 | 9 | include HTTP::Handler 10 | 11 | def initialize(@session_key = Constant::SESSION_KEY, secret_key = Constant::SESSION_SECRET_KEY) 12 | @encoder = Core::Helper::Encoder.new(secret_key) 13 | end 14 | 15 | def call(context) 16 | context.session = get_session(context.request.cookies) 17 | data = @encoder.hex_digest(context.session.to_json) 18 | if next_handler = @next 19 | next_handler.call(context) 20 | end 21 | set_session(context.response, context.session, data) 22 | end 23 | 24 | private def get_session(cookies) : Hash(String, String) 25 | if cookie = cookies[@session_key]? 26 | Hash(String, String).from_json(@encoder.decode(cookie.value)) 27 | else 28 | Hash(String, String).new 29 | end 30 | end 31 | 32 | private def set_session(response, session, data) 33 | data = session.to_json 34 | return if data == @encoder.hex_digest(data) 35 | response.set_cookie(@session_key, @encoder.encode(data)) 36 | end 37 | 38 | private def delete_session 39 | response.delete_cookie(@session_key) 40 | end 41 | end 42 | end 43 | end 44 | end 45 | 46 | class HTTP::Server::Context 47 | property! session : Hash(String, String) 48 | end -------------------------------------------------------------------------------- /src/mustafa/core/helper/model.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Core 3 | module Helper 4 | class Model 5 | INSTANCE = Model.new 6 | 7 | property :__models_obj 8 | property :__models 9 | 10 | def initialize 11 | @__models_obj = {} of Mustafa::Core::Model.class => Mustafa::Core::Model 12 | @__models = {} of Mustafa::Core::Model.class => Mustafa::Core::Model.class 13 | @__models_scope = {} of Mustafa::Core::Model.class => Mustafa::Core::Model::Scope 14 | end 15 | 16 | ### 17 | # Controller register and load methods 18 | # 19 | # this method use polymorphysm 20 | ### 21 | def register_model (model_class : Mustafa::Core::Model.class, scope : Mustafa::Core::Model::Scope) 22 | @__models[model_class] = model_class 23 | @__models_scope[model_class] = scope 24 | puts "Model is registed : #{model_class}" 25 | end 26 | 27 | def load_model (name : Mustafa::Core::Model.class) 28 | if @__models_scope[name].value == Mustafa::Core::Model::Scope::Singleton.value 29 | @__models_obj[name] 30 | elsif @__models_scope[name].value == Mustafa::Core::Model::Scope::Request.value 31 | @__models[name].new 32 | elsif @__models_scope[name].value == Mustafa::Core::Model::Scope::Prototype.value 33 | @__models_obj[name]#.clone 34 | end 35 | end 36 | 37 | def get_model_scope(name : Mustafa::Core::Model.class) : Mustafa::Core::Model::Scope 38 | @__models_scope[name] 39 | end 40 | end 41 | 42 | def self.model 43 | yield Model::INSTANCE 44 | end 45 | 46 | def self.model 47 | Model::INSTANCE 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /src/mustafa/input/delete.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Input 3 | class DELETE 4 | INSTANCE = DELETE.new 5 | 6 | getter :params 7 | 8 | def initialize 9 | @params = {} of String => Array(String) 10 | end 11 | 12 | ### 13 | # this method return post query parameters 14 | # 15 | ### 16 | def [](key : String, index : Int32) : String 17 | if @params.has_key?(key) 18 | if @params[key].size > index 19 | @params[key][index] 20 | else 21 | "" 22 | end 23 | else 24 | "" 25 | end 26 | end 27 | 28 | ### 29 | # this method return only a post query parameter 30 | # 31 | ### 32 | def [](key : String) : String 33 | if @params.has_key?(key) 34 | @params[key][0] 35 | else 36 | "" 37 | end 38 | end 39 | 40 | ### 41 | # this method is implement for develop envoriment 42 | ### 43 | def set_with_query(query : String) 44 | @params = {} of String => Array(String) 45 | 46 | HTTP::Params.parse(query) do |key, value| 47 | ary = @params[key] ||= [] of String 48 | ary.push value 49 | end 50 | end 51 | end 52 | 53 | def self.delete 54 | yield DELETE::INSTANCE 55 | end 56 | 57 | def self.delete 58 | DELETE::INSTANCE 59 | end 60 | 61 | end 62 | end -------------------------------------------------------------------------------- /src/mustafa/core/helper/library.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Core 3 | module Helper 4 | class Library 5 | INSTANCE = Library.new 6 | 7 | property __libraries_class 8 | property __libraries_obj 9 | 10 | def initialize 11 | @__libraries_obj = {} of Mustafa::Core::Library.class => Mustafa::Core::Library 12 | @__libraries_class = {} of Mustafa::Core::Library.class => Mustafa::Core::Library.class 13 | @__libraries_scope = {} of Mustafa::Core::Library.class => Mustafa::Core::Library::Scope 14 | end 15 | 16 | ### 17 | # Library register and load methods 18 | # 19 | # this method use polymorphysm 20 | ### 21 | def register_library (library_class : Mustafa::Core::Library.class, scope : Mustafa::Core::Library::Scope) 22 | @__libraries_class[library_class] = library_class 23 | @__libraries_scope[library_class] = scope 24 | puts "Library is registed : #{library_class}" 25 | end 26 | 27 | def load_library(library_class : Mustafa::Core::Library.class) 28 | if @__libraries_scope[library_class].value == Mustafa::Core::Library::Scope::Singleton.value 29 | @__libraries_obj[library_class] 30 | elsif @__libraries_scope[library_class].value == Mustafa::Core::Library::Scope::Request.value 31 | @__libraries_class[library_class].new 32 | end 33 | end 34 | 35 | def get_library_scope(name : Mustafa::Core::Library.class) : Mustafa::Core::Library::Scope 36 | @__libraries_scope[name] 37 | end 38 | end 39 | 40 | def self.library 41 | yield Library::INSTANCE 42 | end 43 | 44 | def self.library 45 | Library::INSTANCE 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /src/mustafa/library/mail.cr: -------------------------------------------------------------------------------- 1 | require "smtp" 2 | 3 | module Mustafa 4 | module Library 5 | class Mail < Mustafa::Core::Library 6 | 7 | init Mail, Core::Library::Scope::Singleton 8 | 9 | getter host : String 10 | 11 | ### 12 | # this class is singletone 13 | # initialize with host and use this host for all connect 14 | # 15 | # Library.mail.host = "localhost" --set only once or use default value (localhost) 16 | # 17 | # Library.mail.send("mustafa@mustafavc.com", "mustafa", "kemal@mustafavc.com", "kemal", "merhaba", "selam") 18 | ### 19 | 20 | def initialize() 21 | @host = "localhost" 22 | end 23 | 24 | def send(host : String, from : String, from_name : String, to : String, to_name : String, subject : String, body : String) 25 | client = SMTP::Client.new(host) 26 | 27 | message = SMTP::Message.new() 28 | 29 | message.from = SMTP::Address.new(email = from, name = from_name) 30 | message.to << SMTP::Address.new(email = to, name = to_name) 31 | message.subject = subject 32 | message.body = body 33 | 34 | client.send message 35 | end 36 | 37 | def send(from : String, from_name : String, to : String, to_name : String, subject : String, body : String) 38 | client = SMTP::Client.new(@host) 39 | 40 | message = SMTP::Message.new() 41 | 42 | message.from = SMTP::Address.new(email = from, name = from_name) 43 | message.to << SMTP::Address.new(email = to, name = to_name) 44 | message.subject = subject 45 | message.body = body 46 | 47 | client.send message 48 | end 49 | 50 | end 51 | end 52 | end -------------------------------------------------------------------------------- /src/mustafa/core/app.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Core 3 | ### 4 | # App is base class for Mustafa 5 | # This class is singletone. Mustafa::Core.app 6 | ### 7 | class App 8 | 9 | INSTANCE = App.new 10 | 11 | ### 12 | # 13 | ### 14 | private def initialize_framework 15 | initialize_controllers 16 | initialize_models 17 | initialize_libraries 18 | end 19 | 20 | private def initialize_controllers 21 | Helper.controller.__controller_names.each do |key, value| 22 | Helper.controller.__controllers[value] = key.new(Helper.controller.__tmp_actions[key]) 23 | end 24 | 25 | Helper.controller.__tmp_actions.clear 26 | #Core.router.__controller_names.clear 27 | end 28 | 29 | private def initialize_models 30 | Helper.model.__models.each do |key, value| 31 | scope = Helper.model.get_model_scope(key) 32 | if scope == Model::Scope::Singleton 33 | Helper.model.__models_obj[key] = value.new 34 | elsif scope == Model::Scope::Prototype 35 | Helper.model.__models_obj[key] = value.new 36 | end 37 | end 38 | end 39 | 40 | private def initialize_libraries 41 | Helper.library.__libraries_class.each do |key, value| 42 | scope = Helper.library.get_library_scope(key) 43 | if scope == Library::Scope::Singleton 44 | Helper.library.__libraries_obj[key] = value.new 45 | end 46 | end 47 | end 48 | 49 | ### 50 | # HTTP Connect methods 51 | # 52 | ### 53 | def serve(port) 54 | initialize_framework 55 | puts "Mustafa is initialized!" 56 | 57 | server = HTTP::Server.new Config::LOCALHOST_ADDRESS, port, [ 58 | HTTP::ErrorHandler.new, 59 | HTTP::LogHandler.new, 60 | Http::Session::Handler.new, 61 | Http::Handler.new, 62 | HTTP::StaticFileHandler.new("", false), 63 | ] 64 | server.listen 65 | end 66 | end 67 | 68 | def self.app 69 | yield App::INSTANCE 70 | end 71 | 72 | def self.app 73 | App::INSTANCE 74 | end 75 | 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /src/mustafa/core/helper/route.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Core 3 | module Helper 4 | class Route 5 | INSTANCE = Route.new 6 | 7 | def set_query_params(method : String, query : String) 8 | if method == "GET" 9 | Input.get.set_with_query query 10 | elsif method == "POST" 11 | Input.post.set_with_query query 12 | elsif method == "PUT" 13 | Input.put.set_with_query query 14 | elsif method == "DELETE" 15 | Input.delete.set_with_query query 16 | end 17 | end 18 | 19 | def path_split(path : String) : Array 20 | path_parse_array = [] of String 21 | 22 | path_parse_array = path.split('/') 23 | 24 | if path_parse_array.size > 1 #TODO 25 | path_parse_array.shift 26 | if path_parse_array.size == 1 27 | if path_parse_array[0].empty? 28 | path_parse_array[0] = "#{Mustafa::Config::MODULE_NAME}::#{Mustafa::Config::DEFAULT_CONTROLLER}" 29 | else 30 | path_parse_array[0] = "#{Mustafa::Config::MODULE_NAME}::#{path_parse_array[0].capitalize}" 31 | end 32 | path_parse_array.insert(1, "index") 33 | else 34 | path_parse_array[0] = "#{Mustafa::Config::MODULE_NAME}::#{path_parse_array[0].capitalize}" 35 | end 36 | else 37 | path_parse_array.insert(0, "#{Mustafa::Config::MODULE_NAME}::#{Mustafa::Config::DEFAULT_CONTROLLER}") 38 | path_parse_array.insert(1, "index") 39 | end 40 | 41 | path_parse_array[1] = path_parse_array[1].downcase 42 | 43 | return path_parse_array 44 | end 45 | 46 | def set_url_params(path : String) 47 | path_parse_array = [] of String 48 | 49 | path_parse_array = path.split('/') 50 | 51 | if path_parse_array.size > 3 #TODO 52 | path_parse_array.shift 3 53 | Input.params.set_params path_parse_array 54 | end 55 | end 56 | end 57 | 58 | def self.route 59 | yield Route::INSTANCE 60 | end 61 | 62 | def self.route 63 | Route::INSTANCE 64 | end 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /src/mustafa/core/helper/controller.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Core 3 | module Helper 4 | class Controller 5 | INSTANCE = Controller.new 6 | 7 | property :__tmp_actions 8 | property :__controller_names 9 | property :__controllers 10 | 11 | def initialize 12 | @__controllers = {} of String => Mustafa::Core::Controller 13 | @__controller_names = {} of Mustafa::Core::Controller.class => String 14 | @__tmp_actions = {} of Mustafa::Core::Controller.class => Hash(String, Proc(Nil)) 15 | end 16 | 17 | def controller_name?(path : String) : Bool 18 | path_parse_array = [] of String 19 | 20 | path_parse_array = path.split('/') 21 | 22 | if path_parse_array.last(1).includes?('.') | path_parse_array.last(1).includes?(',') 23 | return false 24 | end 25 | 26 | i = 0 27 | while (i < path_parse_array.size) 28 | if path_parse_array[i].includes?('.') | path_parse_array[i].includes?(',') 29 | return false 30 | end 31 | i += 1 32 | end 33 | 34 | return true 35 | end 36 | 37 | ### 38 | # Controller register and load methods 39 | # 40 | # this method use polymorphysm 41 | ### 42 | def register_controller (name : String, controller_class : Mustafa::Core::Controller.class) 43 | @__controller_names[controller_class] = name 44 | @__tmp_actions[controller_class] = Hash(String, Proc(Nil)).new 45 | puts "Controller is registed : #{name}" 46 | end 47 | 48 | def register_action (controller_class : Mustafa::Core::Controller.class, name : String, &block) 49 | if !@__tmp_actions[controller_class].has_key?(name) 50 | @__tmp_actions[controller_class][name] = block 51 | puts "Action is registed. #{name}" 52 | else 53 | Mustafa::Library.log.add("Action is already exist.") 54 | puts "Action is already exist." 55 | end 56 | end 57 | end 58 | 59 | def self.controller 60 | yield Controller::INSTANCE 61 | end 62 | 63 | def self.controller 64 | Controller::INSTANCE 65 | end 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /src/mustafa/library/db/mysql.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Library 3 | module Database 4 | class Mysql < Core::IDatabase 5 | 6 | @db : DB::Database 7 | 8 | def initialize(connection_string : String) 9 | @db = DB.open connection_string 10 | end 11 | 12 | def query(query_string : String) : Array(Hash(String, String)) 13 | read_query_result(query_string) 14 | end 15 | 16 | def select(select_columns : String, from : String, where = "true") : Array(Hash(String, String)) 17 | read_query_result("select #{select_columns} from #{from} where #{where}") 18 | end 19 | 20 | def select_all(from : String, where = "true") : Array(Hash(String, String)) 21 | read_query_result("select * from #{from} where #{where}") 22 | end 23 | 24 | def select_table_count(table : String) 25 | @db.scalar "select max(*) from #{table}" 26 | end 27 | 28 | def select_count(table : String, where : String) 29 | @db.scalar "select max(*) from #{table} where #{where}" 30 | end 31 | 32 | def insert(table : String, values : Array(DB::Any)) 33 | @db.exec "insert into #{table} values (?, ?)", values 34 | end 35 | 36 | def delete(table : String, where = "false") 37 | @db.exec "delete from #{table} where #{where}" 38 | end 39 | 40 | def close 41 | @db.close 42 | end 43 | 44 | private def read_query_result(query_string : String) : Array(Hash(String, String)) 45 | ret_val = Array(Hash(String, String)).new 46 | 47 | @db.query query_string do |rs| 48 | rs.each do 49 | column_count = 0 50 | hash = {} of String => String 51 | while column_count != rs.column_count 52 | hash["#{rs.column_name(column_count)}"] = "#{rs.read}" 53 | column_count += 1 54 | end 55 | ret_val << hash 56 | end 57 | end 58 | 59 | return ret_val 60 | end 61 | end 62 | end 63 | end 64 | end -------------------------------------------------------------------------------- /src/mustafa/library/db/sqllite.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Library 3 | module Database 4 | class Sqlite < Core::IDatabase 5 | 6 | @db : DB::Database 7 | 8 | def initialize(connection_string = "sqlite3://./data.db") 9 | @db = DB.open connection_string 10 | end 11 | 12 | def query(query_string : String) : Array(Hash(String, String)) 13 | read_query_result(query_string) 14 | end 15 | 16 | def select(select_columns : String, from : String, where = "true") : Array(Hash(String, String)) 17 | read_query_result("select #{select_columns} from #{from} where #{where}") 18 | end 19 | 20 | def select_all(from : String, where = "true") : Array(Hash(String, String)) 21 | read_query_result("select * from #{from} where #{where}") 22 | end 23 | 24 | def select_table_count(table : String) 25 | @db.scalar "select max(*) from #{table}" 26 | end 27 | 28 | def select_count(table : String, where : String) 29 | @db.scalar "select max(*) from #{table} where #{where}" 30 | end 31 | 32 | def insert(table : String, values : Array(DB::Any)) 33 | @db.exec "insert into #{table} values (?, ?)", values 34 | end 35 | 36 | def delete(table : String, where = "false") 37 | @db.exec "delete from #{table} where #{where}" 38 | end 39 | 40 | def close 41 | @db.close 42 | end 43 | 44 | private def read_query_result(query_string : String) : Array(Hash(String, String)) 45 | ret_val = Array(Hash(String, String)).new 46 | 47 | @db.query query_string do |rs| 48 | rs.each do 49 | column_count = 0 50 | hash = {} of String => String 51 | while column_count != rs.column_count 52 | hash["#{rs.column_name(column_count)}"] = "#{rs.read}" 53 | column_count += 1 54 | end 55 | ret_val << hash 56 | end 57 | end 58 | 59 | return ret_val 60 | end 61 | end 62 | end 63 | end 64 | end -------------------------------------------------------------------------------- /src/mustafa/core/view.cr: -------------------------------------------------------------------------------- 1 | require "ecr" 2 | require "ecr/macros" 3 | 4 | module Mustafa 5 | module Core 6 | abstract class View 7 | 8 | getter view_params = {} of String => String 9 | getter view_arrays = {} of String => Array(String) 10 | getter view_hashes = {} of String => Hash(String, String) 11 | getter view_controls = {} of String => Core::Control 12 | getter script = "" 13 | getter css = "" 14 | 15 | property content_type = "text/html" 16 | property content_length = 0 17 | property status_code = 200 18 | 19 | ### 20 | # 21 | ### 22 | def script=(val : String) 23 | @script = @script + val 24 | end 25 | 26 | def css=(val : String) 27 | @css = @css + val 28 | end 29 | 30 | ### 31 | # this method is abstract. it use for initialize view s values 32 | # 33 | ### 34 | abstract def load 35 | 36 | ### 37 | # This method to add param to view 38 | ### 39 | def add_param(key : String, value : String) 40 | @view_params[key] = value 41 | end 42 | 43 | ### 44 | # This method to send array to view 45 | ### 46 | def add_array_param(key : String, value : Array(String)) 47 | @view_arrays[key] = value 48 | end 49 | 50 | ### 51 | # This method to send hash to view 52 | ### 53 | def add_hash_param(key : String, value : Hash(String)) 54 | @view_arrays[key] = value 55 | end 56 | 57 | ### 58 | # 59 | ### 60 | def add_user_control(control_name : String, control_class : Core::Control.class) 61 | @view_controls[control_name] = control_class.new 62 | @script = @script + "\n\n" + @view_controls[control_name].script 63 | yield @view_controls[control_name] 64 | end 65 | 66 | ### 67 | # 68 | ### 69 | def add_user_control(control_name : String, control_class : Core::Control.class) 70 | @view_controls[control_name] = control_class.new 71 | @script = @script + "\n\n" + @view_controls[control_name].script 72 | end 73 | 74 | ### 75 | # 76 | ### 77 | def add_user_controls(controls : Hash(String, Core::Control.class)) 78 | controls.each do |key, value| 79 | @view_controls[key] = value.new 80 | @script = @script + "\n\n" + @view_controls[control_name].script 81 | end 82 | end 83 | 84 | ### 85 | # This macro is for initialize ECR View class 86 | # 87 | # init "Welcomeview.ecr" 88 | ### 89 | macro init(filename) 90 | def initialize () 91 | 92 | end 93 | 94 | ECR.def_to_s "./#{Config::VIEW_PATH.id}/#{{{filename}}}" 95 | end 96 | end 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /src/mustafa/core/controller.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Core 3 | abstract class Controller 4 | 5 | getter view : Mustafa::Core::View 6 | 7 | def initialize(actions : Hash(String, Proc(Nil))) 8 | @view = Mustafa::Notfoundview.new 9 | @__actions = actions 10 | end 11 | 12 | ### 13 | # this macro registed controller on system 14 | # 15 | # class WelcomeController < Controller 16 | # init WelcomeController 17 | # ... 18 | # end 19 | ### 20 | macro init(controller_name) 21 | Core::Helper.controller.register_controller "#{{{controller_name}}}", {{controller_name.id}} 22 | 23 | def initialize(actions : Hash(String, Proc(Nil))) 24 | super(actions) 25 | end 26 | end 27 | 28 | ### 29 | # this macro registered controller on system and create view for following controller 30 | # 31 | # class Welcomecontroller < Controller 32 | # init Welcomecontroller, "Welcome.ecr" 33 | ### 34 | macro init(controller_name, filename) 35 | class {{controller_name.id}}view < Core::View 36 | def load 37 | end 38 | 39 | ECR.def_to_s "./#{Config::VIEW_PATH.id}/#{{{filename}}}" 40 | end 41 | 42 | Core::Helper.controller.register_controller "#{{{controller_name}}}", {{controller_name.id}} 43 | 44 | def initialize(actions : Hash(String, Proc(Nil))) 45 | super(actions) 46 | end 47 | end 48 | 49 | macro action (name, &block) 50 | Core::Helper.controller.register_action(self, {{name}}) {{block}} 51 | end 52 | 53 | ### 54 | # this macro load action 55 | # 56 | # action is controller method on Mustafa 57 | # 58 | # Example for use; 59 | # Mustafa::Controller.run Welcomecontroller, "index" 60 | ### 61 | def run_action(method_name : String) 62 | if @__actions.has_key?(method_name) 63 | if callback = @__actions[method_name] 64 | callback.call 65 | end 66 | else 67 | Mustafa::Library.log.add("There is no action : #{method_name}") 68 | puts "There is no action : #{method_name}" 69 | #show_404 70 | end 71 | rescue ex 72 | Mustafa::Library.log.add("on Action Error Handling (#{method_name}) : #{ex.message}") 73 | end 74 | 75 | ### 76 | # 77 | ### 78 | def register_view(view_class : Mustafa::Core::View.class) 79 | @view = view_class.new 80 | puts "#{view_class} view is registed for #{self} controller!" 81 | end 82 | 83 | ### 84 | # 85 | ### 86 | def register_view(view_class : Mustafa::Core::View.class) 87 | @view = view_class.new 88 | yield @view 89 | puts "#{view_class} view is registed for #{self} controller!" 90 | end 91 | 92 | ### 93 | # 94 | ### 95 | def register_json(json_output : String) 96 | @view = JSONview.new(json_output) 97 | puts "JSON output for #{self} controller!" 98 | end 99 | 100 | ### 101 | # 102 | ### 103 | def load_view : Mustafa::Core::View 104 | @view.load 105 | @view 106 | end 107 | 108 | ### 109 | # this macro create base view class 110 | # 111 | ### 112 | macro init_base_view(name, filename) 113 | class {{name.id}} < Core::View 114 | def load 115 | end 116 | 117 | ECR.def_to_s "./#{Config::VIEW_PATH.id}/#{{{filename}}}" 118 | end 119 | end 120 | 121 | end 122 | end 123 | end 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # MustafaVC 4 | 5 | Mustafa is MVC Framework for Crystal 6 | 7 | Please contribute project. [MustafaVC Fork](https://github.com/guvencenanguvenal/mustafa/fork) 8 | 9 | [TODO](https://github.com/guvencenanguvenal/mustafa#todo) 10 | 11 | # Getting Started :sunglasses: 12 | 13 | Download [Visual Studio Code](https://code.visualstudio.com/download) and add [Crystal](https://github.com/g3ortega/vscode-crystal) extension for faster coding with [snippets](https://github.com/guvencenanguvenal/mustafa/blob/master/snippets.md). 14 | 15 | Add your `shard.yml` and install lib. Yes it's ready! Build your MVC project. 16 | 17 | For more information, you can see [Mustafa Wiki](https://github.com/guvencenanguvenal/mustafa/wiki) 18 | 19 | # Installation :star: 20 | 21 | Add this to your application's `shards.yml` 22 | 23 | ```yaml 24 | dependencies: 25 | mustafa: 26 | github: guvencenanguvenal/mustafa 27 | branch: master 28 | ``` 29 | 30 | # Route :rocket: 31 | 32 | *http://localhost/welcome* 33 | 34 | call controller which name is Welcome 35 | 36 | *http://localhost/welcome/hello* 37 | 38 | call controller which name is Welcome and run action(method) which name is "hello" 39 | 40 | *http://localhost/welcome/hello/param1/param2* 41 | 42 | call controller which name is Welcome and run action(method) which name is "hello" and.. 43 | 44 | ```crystal 45 | Input.params[0] => "param1" 46 | Input.params[1] => "param2" 47 | ``` 48 | 49 | >**Note:** params is not get or post 50 | 51 | # Configuration or Use Template :mag_right: 52 | 53 | Configuration file is config.cr on `src/mustafa/config/config.cr` 54 | 55 | ```crystal 56 | DEFAULT_CONTROLLER = "your default controller" #this controller is default which response http://localhost/ 57 | MODULE_NAME = "your module name" 58 | VIEW_PATH = "src/your module name/view" # ./src/mustafa/view/ecr_file.ecr 59 | ``` 60 | 61 | and choose your localhost 62 | 63 | ``` 64 | LOCALHOST_ADDRESS = "0.0.0.0" 65 | ``` 66 | 67 | or 68 | 69 | You can use [Mustafa Template](https://github.com/guvencenanguvenal/mustafatemplate) to faster start. 70 | 71 | # Super Simple :checkered_flag: 72 | 73 | ## Model 74 | 75 | Model name must be capitalized case (first letter is upper, other letters is lower) 76 | 77 | `Welcomemodel.cr` 78 | 79 | ```crystal 80 | class Welcomemodel < Core::Model 81 | init Welcomemodel, Core::Model::Scope::Singleton 82 | 83 | def hello 84 | puts "hello" 85 | end 86 | end 87 | ``` 88 | 89 | ## Controller 90 | 91 | Controller name must be capitalized case (first letter is upper, other letters is lower) 92 | 93 | `Welcomecontroller.cr` 94 | 95 | ```crystal 96 | class Welcomecontroller < Core::Controller 97 | init Welcomecontroller 98 | 99 | action "index" do 100 | 101 | Core.loader.library(Customlib) do |library| 102 | library.as(Customlib).foo 103 | end 104 | 105 | Core.loader.model(Welcomemodel) do |model| 106 | model.as(Welcomemodel).hello 107 | end 108 | 109 | Core.loader.view(self, Welcomeview) 110 | end 111 | end 112 | ``` 113 | 114 | ## View 115 | 116 | View has a class and an ECR File 117 | 118 | `Welcomeview.cr` 119 | 120 | ```crystal 121 | class Welcomeview < Core::View 122 | init "Welcome.ecr" 123 | 124 | def load 125 | if (view_params["key"] == "value") 126 | puts "hello" #put hello on terminal 127 | end 128 | end 129 | end 130 | ``` 131 | 132 | `Welcome.ecr` 133 | 134 | ```crystal 135 | Selam, <%= @view_params["key"] %>! # value 136 | ``` 137 | 138 | ## Run, go go go 139 | 140 | `Yourproject.cr` 141 | 142 | ```crystal 143 | require "mustafa" 144 | #And require your controller, view and model files 145 | 146 | module Yourproject 147 | Mustafa.run 148 | end 149 | ``` 150 | 151 | # What's new with the new version 152 | 153 | * Create your custom library 154 | * Select your model scope 155 | * New loader system 156 | * It's now faster than before 157 | 158 | # TODO 159 | 160 | ``` 161 | [ ] Library improve 162 | [ ] Database Library improve 163 | [ ] Oracle Driver implement 164 | [ ] add UserControl 165 | ``` 166 | 167 | # Contribute 168 | 169 | Please fork project [MustafaVC](https://github.com/guvencenanguvenal/mustafa/fork) 170 | 171 | # Thanks 172 | 173 | Thanks [porras/session](https://github.com/porras/session) for session and encoder class. 174 | 175 | Thanks [logomakr.com](http://logomakr.com) for logo. 176 | 177 | 178 | -------------------------------------------------------------------------------- /src/mustafa/core/loader.cr: -------------------------------------------------------------------------------- 1 | module Mustafa 2 | module Core 3 | class Loader 4 | 5 | INSTANCE = Loader.new 6 | 7 | def initialize 8 | 9 | end 10 | 11 | ### 12 | # load model and return 13 | ### 14 | def model(model_name : Model.class) 15 | _model = Helper.model.load_model model_name 16 | if _model 17 | return _model 18 | else 19 | Mustafa::Library.log.add("Model is not found! #{model_name}", Mustafa::LogType::Application.value) 20 | raise "Model is not found!" 21 | end 22 | end 23 | 24 | ### 25 | # load model with block 26 | ### 27 | def model(model_name : Model.class) 28 | _model = Helper.model.load_model model_name 29 | 30 | if _model 31 | yield _model 32 | else 33 | Mustafa::Library.log.add("Model is not found! #{model_name}", Mustafa::LogType::Application.value) 34 | raise "Model is not found!" 35 | end 36 | end 37 | 38 | ### 39 | # load view and return 40 | ### 41 | def view(controller_class : Core::Controller.class, view_class : Core::View.class) 42 | Helper.controller.__controllers[Helper.controller.__controller_names[controller_class]].register_view view_class 43 | end 44 | 45 | ### 46 | # load view with block 47 | ### 48 | def view(controller_class : Core::Controller.class, view_class : Core::View.class) 49 | Helper.controller.__controllers[Helper.controller.__controller_names[controller_class]].register_view view_class do |view| 50 | yield view 51 | end 52 | end 53 | 54 | ### 55 | # load json view 56 | ### 57 | def json(controller_class : Core::Controller.class, json_output : String) 58 | Helper.controller.__controllers[Helper.controller.__controller_names[controller_class]].register_json json_output 59 | end 60 | 61 | ### 62 | # load single library and return 63 | ### 64 | def library(library_name : Core::Library.class) 65 | _lib = Helper.library.load_library(library_name) 66 | if _lib 67 | return _lib 68 | else 69 | Mustafa::Library.log.add("Model is not found! #{library_name}", Mustafa::LogType::Application.value) 70 | raise "Library is not found!" 71 | end 72 | end 73 | 74 | ### 75 | # load single library with block 76 | ### 77 | def library(library_name : Core::Library.class) 78 | _lib = Helper.library.load_library(library_name) 79 | if _lib 80 | yield _lib 81 | else 82 | Mustafa::Library.log.add("Model is not found! #{library_name}", Mustafa::LogType::Application.value) 83 | raise "Library is not found!" 84 | end 85 | end 86 | 87 | ### 88 | # load multiple libraries and return with hash 89 | ### 90 | def library(library_names : Array(Core::Library.class)) 91 | _libraries = Hash(Core::Library.class, Core::Library).new 92 | 93 | library_names.each do |name| 94 | _lib = Helper.library.load_library(name) 95 | 96 | if _lib 97 | _libraries[name] = _lib 98 | end 99 | end 100 | 101 | yield _libraries 102 | end 103 | 104 | ### 105 | # load multiple libraries with block and return with hash 106 | ### 107 | def library(library_names : Array(Core::Library.class)) : Hash(Core::Library.class, Core::Library) 108 | _libraries = Hash(Core::Library.class, Core::Library).new 109 | 110 | library_names.each do |name| 111 | _lib = Helper.library.load_library(name) 112 | 113 | if _lib 114 | _libraries[name] = _lib 115 | end 116 | end 117 | 118 | _libraries 119 | end 120 | 121 | @__connetion_string = Constant::DB_CONNECTION_STRING 122 | @__db_class = Constant::DB_CLASS 123 | ### 124 | # 125 | ### 126 | def db : Core::IDatabase 127 | _db = @__db_class.new(@connetion_string) 128 | 129 | return _db 130 | rescue DB::ConnectionRefused 131 | Mustafa::Library.log.add("Database is not conneting : DB Type : #{@__db_class}", Mustafa::LogType::System.value) 132 | end 133 | 134 | ### 135 | # 136 | ### 137 | def db 138 | _db = @__db_class.new(@__connetion_string) 139 | yield _db 140 | _db.close 141 | 142 | rescue DB::ConnectionRefused 143 | Mustafa::Library.log.add("Database is not conneting : DB Type : #{@__db_class}", Mustafa::LogType::System.value) 144 | end 145 | 146 | ### 147 | # 148 | ### 149 | def db(db_class : Core::IDatabase.class, connetion_string : String) : Core::IDatabase 150 | @__db_class = db_class 151 | @__connetion_string = connetion_string 152 | 153 | _db = db_class.new(connetion_string) 154 | 155 | return _db 156 | rescue DB::ConnectionRefused 157 | Mustafa::Library.log.add("Database is not conneting : DB Type : #{db_class}", Mustafa::LogType::System.value) 158 | end 159 | 160 | ### 161 | # 162 | ### 163 | def db(db_class : Core::IDatabase.class, connetion_string : String) 164 | @__db_class = db_class 165 | @__connetion_string = connetion_string 166 | 167 | __db = db_class.new(connetion_string) 168 | yield _db 169 | _db.close 170 | 171 | rescue DB::ConnectionRefused 172 | Mustafa::Library.log.add("Database is not conneting : DB Type : #{db_class}", Mustafa::LogType::System.value) 173 | end 174 | end 175 | 176 | def self.loader 177 | Loader::INSTANCE 178 | end 179 | 180 | def self.loader 181 | yield Loader::INSTANCE 182 | end 183 | end 184 | end 185 | --------------------------------------------------------------------------------