Showing posts with label laravel. Show all posts
Showing posts with label laravel. Show all posts

Thursday, November 26, 2015

Why I moved from capistrano to mina to deploy my app?


To be honest, I have just started to use deployment tools because doing the same thing over and over again was getting very tedious for me. I first started with capistrano and it was good I guess.

Why I prefer Mina over Capistrano?

While Capistrano is a really really great tool , it is terribly slow. Since time is money for every one of us, I wanted a tool that would not be so painfully slow and easy to use, you know , that get shits done. Then I met Mina and instantly fell in love with it when I browsed its homepage and read its slogan "Really fast deployer  and server automation tool" .

Before I was using any deployment tools I was manually ssh`ing into my server and then cloning the repo from the git then running bundle install , rake db:migrate and a lot of stuffs that is not very interesting to do repeatedly. If I had pushed any new updates to the repo I would have to ssh into the server and do the same thing which is time consuming as well as not very productive. So I highly reckon anyone who is not using deployment tools to use it and feel its power. But remember Great power comes with Great responsibility. :)

It is dead simple to get started with Mina. All you have to do is add a gem run the bundler, create a deploy.rb file , fill the right info in it and running mina deploy. The best part is you can write rake tasks in deploy.rb.

Either run gem install mina or add mina gem in your gemfile.
Run bundle install if you chose the later option.
run mina init in your working directory
Here is my deploy.rb file
require 'mina/bundler'
require 'mina/rails'
require 'mina/git'

# Basic settings:
#   domain       - The hostname to SSH to.
#   deploy_to    - Path to deploy into.
#   repository   - Git repo to clone from. (needed by mina/git)
#   branch       - Branch name to deploy. (needed by mina/git)

set :user, 'ubuntu'
set :domain, 'www.fuitter.com'
set :deploy_to, '/usr/share/nginx/html/fuitter'
set :repository, 'git@bitbucket.org:mc_cannibal/fuitter2.git'
set :branch, 'master'
set :forward_agent, true

# Manually create these paths in shared/ (eg: shared/config/database.yml) in your server.
# They will be linked in the 'deploy:link_shared_paths' step.
set :shared_paths, ['config/database.yml', 'config/secrets.yml', 'log']

# Optional settings:
#   set :user, 'foobar'    # Username in the server to SSH to.
#   set :port, '30000'     # SSH port number.
#   set :forward_agent, true     # SSH forward_agent.

# This task is the environment that is loaded for most commands, such as
# `mina deploy` or `mina rake`.
task :environment do
  ruby_version = File.read('.ruby-version').strip
  raise "Couldn't determine Ruby version: Do you have a file .ruby-version in your project root?" if ruby_version.empty?
 queue %{
   source /home/ubuntu/.rvm/bin/rvm
   rvm use #{ruby_version} || exit 1
 }
end

task :setup => :environment do
  queue! %[mkdir -p "#{deploy_to}/#{shared_path}/log"]
  queue! %[chmod g+rx,u+rwx "#{deploy_to}/#{shared_path}/log"]

  queue! %[mkdir -p "#{deploy_to}/#{shared_path}/config"]

  # Add the repository server to .ssh/known_hosts
  if repository
    repo_host = repository.split(%r{@|://}).last.split(%r{:|\/}).first
    repo_port = /:([0-9]+)/.match(repository) && /:([0-9]+)/.match(repository)[1] || '22'

    queue! %[
      if ! ssh-keygen -H  -F #{repo_host} &>/dev/null; then
        ssh-keyscan -t rsa -p #{repo_port} -H #{repo_host} >> ~/.ssh/known_hosts
      fi
    ]
  end

  # Create database.yml for Postgres if it doesn't exist
  path_database_yml = "#{deploy_to}/#{shared_path}/config/database.yml"
  database_yml = %[production:
  database: fuitter
  adapter: postgresql
  pool: 5
  timeout: 5000]
  queue! %[ test -e #{path_database_yml} || echo "#{database_yml}" > #{path_database_yml} ]

  # Create secrets.yml if it doesn't exist
  path_secrets_yml = "#{deploy_to}/#{shared_path}/config/secrets.yml"
  secret =
  secrets_yml = %[production:
  secret_key_base:
    #{`rake secret`.strip}]
  queue! %[ test -e #{path_secrets_yml} || echo "#{secrets_yml}" > #{path_secrets_yml} ]

  queue! %[chmod g+rx,u+rwx,o-rwx "#{deploy_to}/#{shared_path}/config"]

end

desc "Deploys the current version to the server."
task :deploy => :environment do
  to :before_hook do
    # Put things to run locally before ssh
  end
  deploy do
    # Put things that will set up an empty directory into a fully set-up
    # instance of your project.
    invoke :'git:clone'
    invoke :'deploy:link_shared_paths'
    invoke :'bundle:install'
    invoke :'rails:db_migrate'
    invoke :'rails:assets_precompile'
    invoke :'deploy:cleanup'

    to :launch do
      # queue "mkdir -p #{deploy_to}/#{current_path}/tmp/"
      # queue "service #{user} restart"
    end
  end
end

# For help in making your deploy script, see the Mina documentation:
#
#  - http://nadarei.co/mina
#  - http://nadarei.co/mina/tasks
#  - http://nadarei.co/mina/settings
#  - http://nadarei.co/mina/helpers

then run mina setup and finally mina deploy.
That is all there is to it.

Thursday, October 8, 2015

Laravel for RAD


Just as good as rails, laravel can really speed up the development process. I finished a fully functional real estate system in a day. Yes there were not much requirements plus I did not follow TDD but that is just whole another story.

As laravel provides authentication out of the box, I had to spend couple of minutes configuring it. Since my app required different access control, I used a module that helped me setup the ACL in few minutes. Since CRUD is a frequent visitor I used another composer package for it and again the setup took just few minutes. The crud generator followed repository design pattern.

Importing the project assets were as easy as ABC`s unlike in Rails where you have to understand the esoteric asset pipeline.

Since it did not provided default generated test codes for the codes I generated I may end up solving a bug or may be a crisis in the near future. Rails is much generous with this and Rspec with Capybara makes testing even more fun.

Eloquent makes it even simpler to query from the database.

Package I used for:

acl: Entrust
generator: CRUD generator
data tables: Data Tables


Saturday, September 26, 2015

Getting CKEditor to work with laravel 5.1

I rarely code in PHP these days but I had to use  Laravel, a great framework, for one of my project. My project required a WYSIWYG editor for its blogging section. I thought of giving ckeditor a try. The installation process was fairly simple.  All I had to do was place the necessary files/folders inside public directory (in my case) and then link the ckeditor.js file in my html. You can also use CDN to include ckeditor.The installation process was painless, however I found it very hard to get the file browser working. In this tutorial, I will talk about getting the file browser up and running.

During the time of writing this, my laravel version is 5.1.17(LTS). I am using Linux Mint 17.1 , sublime text 3 as my text editor, nginx as my server and mysql as my database. Except my laravel version and OS, the others are just extraneous, haha.

Ok, click in the editor`s image icon. You will see that there is no upload option.

To activate upload option, you have to add filebrowserImageUploadUrl.

CKEDITOR.replace('editor1',{
        filebrowserImageUploadUrl : "{{route('infos.upload',['_token' => csrf_token() ])}}",
        filebrowserWindowWidth  : 800,
        filebrowserWindowHeight : 500
    });



Here the filebrowserImageUploadUrl , will send a POST request to the route. If you remove the '_token' => csrf_token() you will get a tokenMismatch error.filebrowserWindowWidth & filebrowserWindowHeight is related with the appearance of the popup window`s dimension.

Lets create a route for 'info.upload'.

Route::post('infos/uploadImage',[
        'as' => 'infos.upload',
        'uses' => 'InfoController@uploadImage'
    ]);


uploadImage method in InfoController

public function uploadImage(Request $request)
    {
        $file = $request->file('upload');
        $uploadDestination = public_path() . '/uploads/about';
        $filename = preg_replace('/\s+/', '', $file->getClientOriginalName());
        $fileName = md5($filename) . "_" . $filename;
        $file->move($uploadDestination, $fileName);
    }


All the uploaded files will be upload to uploads/about folder, which is arbitrary.

At this point, your file should be uploaded. If not try opening your developers tool and check the network tab, there you can trace any errors.

Ok, now your file has been uploaded but you have to include it . To do that you have to add certain keys.
CKEDITOR.replace('editor1',{
        filebrowserBrowseUrl: "{{route('infos.image.browse')}}",
        filebrowserUploadUrl : '/browser/upload/type/all',
        filebrowserImageBrowseUrl: "{{route('infos.image.browse')}}",
        filebrowserImageUploadUrl : "{{route('infos.upload',['_token' => csrf_token() ])}}",
        filebrowserWindowWidth  : 800,
        filebrowserWindowHeight : 500
    });


Lets take a look at the routes now.

Route::post('infos/uploadImage',[
        'as' => 'infos.upload',
        'uses' => 'InfoController@uploadImage'
    ]);

    Route::get('infos/image/browse',[
        'as' => 'infos.image.browse',
        'uses' => 'InfoController@browseImage'
    ]);


This will how our controller looks right now:

public function uploadImage(Request $request)
    {
        $file = $request->file('upload');
        $uploadDestination = public_path() . '/uploads/about';
        $filename = preg_replace('/\s+/', '', $file->getClientOriginalName());
        $fileName = md5($filename) . "_" . $filename;
        $file->move($uploadDestination, $fileName);
    }

    public function browseImage(Request $request)
    {
        $test = $_GET['CKEditorFuncNum'];
        $images = [];
        $files = \File::files(public_path() . '/uploads/about');
        foreach ($files as $file) {
            $images[] = pathinfo($file);
        }
        return view('infos.file',[
            'files' => $images,
            'test' => $test
        ]);
     
    }


This will be the contents of our view file
@extends('app')

@section('content')
    @foreach($files as $file)
        <a href='{{url("uploads/about/".$file["basename"])}}'><img src='{{url("uploads/about/".$file["basename"])}}'></a>
    @endforeach
@endsection

@section('footer')
<script type="text/javascript">
$('a[href]').on('click', function(e){
    window.opener.CKEDITOR.tools.callFunction(<?php echo $test; ?>,$(this).find('img').prop('src'))
});
</script>
@endsection


This line of code window.opener.CKEDITOR.tools.callFunction(<?php echo $test; ?>,$(this).find('img').prop('src')) gets the url of clicked image and then put it into the url field.
That`s all there is to it.
If you run into any problem, leave a comment below. I hope to sort it out.